package com.liveinstars.api.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.onepiece.shipelves.common.exception.QuHuiRuntimeException;
import com.onepiece.shipelves.common.exception.SystemException;
import feign.FeignException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import java.util.List;
import java.util.stream.Stream;

/**
 * 可以处理Controller中的异常，但无法处理Filter中的。
 * Filters happens before controllers are even resolved so exceptions thrown from filters can't be caught by a Controller Advice.
 * Created by guoyanjiang on 2018/4/11.
 */
@ControllerAdvice(basePackages = {"com.liveinstars", "com.appserver"})
public class SpringCloudExceptionHandler extends ResponseEntityExceptionHandler {

    Logger logger = LoggerFactory.getLogger(getClass());

    @ExceptionHandler({QuHuiRuntimeException.class})
    ResponseEntity handlerQuhuiException(QuHuiRuntimeException ex, WebRequest request) {
        logger.error("业务异常: {}" ,ex);
        printErrorStack(ex);
        return new ResponseEntity(new CommonResponse(ex.getCode(), ex.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @ExceptionHandler({RuntimeException.class})
    ResponseEntity handlerException(Exception ex, WebRequest request) {
        logger.error("未特定处理的运行时异常: {}" ,ex);
        printErrorStack(ex);
        return new ResponseEntity(new CommonResponse("500", ex.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @ExceptionHandler({NullPointerException.class})
    ResponseEntity handlerNullPointerException(Exception ex, WebRequest request) {
        logger.error("空指针异常:{}", ex);
        printErrorStack(ex);
        return new ResponseEntity(new CommonResponse("500", ex.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
    }

    //用Assert时会抛出此异常，关注其他情景
    @ExceptionHandler({IllegalArgumentException.class})
    ResponseEntity handlerIllegalArgumentException(Exception ex, WebRequest request) {
        logger.error("illegalargumentexception:{}", ex);
        printErrorStack(ex);
        return new ResponseEntity(new CommonResponse("500", ex.getMessage()), HttpStatus.BAD_REQUEST);
    }

    //参数数据类型出错时
    @ExceptionHandler({MethodArgumentTypeMismatchException.class, NumberFormatException.class})
    ResponseEntity handlerMethodArgumentTypeMismatchException(Exception ex, WebRequest request) {
        printErrorStack(ex);
        return new ResponseEntity(new CommonResponse("500", "params type error"), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler({SystemException.class})
    ResponseEntity handlerSystemException(SystemException ex) {
        printErrorStack(ex);
        return new ResponseEntity(ex.getMessage(), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler({JSONException.class})
    ResponseEntity handlerJSONException(Exception ex, WebRequest request) {
        logger.error("JSON转换异常");
//        if (ex.getMessage().contains("<!DOCTYPE html")) {
//            String html = ex.getMessage().substring(ex.getMessage().indexOf("<!DOCTYPE html"));
//            Document document = Jsoup.parse(html);
//            String message = null;
//            try {
//                message = document.select("div#messagetext p").html();
//                if (StringUtils.isEmpty(message)) {
//                    Element element = document.select("div.info").first();
//                    message = element != null ? element.html() : "";
//                }
//                if (StringUtils.isEmpty(message)) {
//                    //http://app_bbs_api.8btc.com/post/newreply 报错时响应的html
//                    Element element = document.select("div.jump_c p").first();
//                    message = element != null ? element.html() : "";
//                }
//                logger.error("解析html错误信息:{}", message);
//                return new ResponseEntity(new CommonResponse(message), HttpStatus.INTERNAL_SERVER_ERROR);
//            } catch (Exception e) {
//                logger.error("尝试解析html失败:{}", html);
//            }
//        } else {
//            printErrorStack(ex);
//        }
        return new ResponseEntity(new CommonResponse("服务器内部异常", ex.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
    }

    //参数校验异常处理
    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        List<ObjectError> errors = ex.getBindingResult().getAllErrors();
        String message = errors.size() > 0 ? errors.get(0).getDefaultMessage() : ex.getMessage();
        return new ResponseEntity(new CommonResponse(message, ex.getMessage()), HttpStatus.BAD_REQUEST);
    }

    @Override
    protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        printErrorStack(ex);
        return new ResponseEntity(new CommonResponse("报文格式有误", ex.getMessage()), HttpStatus.BAD_REQUEST);
    }

    //请求参数漏了时导致绑定失败的异常处理
    @Override
    protected ResponseEntity<Object> handleServletRequestBindingException(ServletRequestBindingException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        printErrorStack(ex);
        return new ResponseEntity(new CommonResponse(ex.getMessage(), ex.getMessage()), HttpStatus.BAD_REQUEST);
    }

    private void printErrorStack(Exception ex) {
        logger.error(ex.getMessage());
        Stream.of(ex.getStackTrace()).filter(element -> element.getClassName().startsWith("com.liveinstars") ).forEach(element -> {
            logger.error(element.toString());
        });
    }

    //注：服务提供者内部抛了CommonException异常后，会被处理为ResponseEntity返回给调用方，然后Feign调用方会将其处理为FeignException
    //这里将FeignException再次处理为ResponseEntity返回，即可实现将异常从微服务内部层层上抛
    //在引入Zuul的Filter之前，暂时先这么处理
    @ExceptionHandler({FeignException.class})
    ResponseEntity handlerFeignException(FeignException ex, WebRequest request) {
        //格式为 status 400 reading HelloService#testException(); content:\n{"message":"hello exception"}
        //第二行即为报文
        logger.error("FeignException {}", ex);
        if (ex.getMessage().contains("message")) {
            String json = ex.getMessage().split("\n")[1];
            String message = JSON.parseObject(json).getString("message");
            int status = ex.status();
            return getResponseEntityFromCommonException(new CommonException(message, status));
        } else {
            logger.error("no exception handler {}", ex);
            logger.error("ex message {}", ex.getMessage());
            return getResponseEntityFromCommonException(new CommonException("未知系统错误", 500));
        }
    }

    @ExceptionHandler({CommonException.class})
    ResponseEntity handlerCommonException(CommonException ex, WebRequest request) {
        return getResponseEntityFromCommonException(ex);
    }

    private ResponseEntity getResponseEntityFromCommonException(CommonException ex) {
        HttpStatus status = HttpStatus.resolve(ex.status());
        if (status == null) {
            status = HttpStatus.BAD_REQUEST;
            logger.error("can not resolve http response code {}", status);
        }
        printErrorStack(ex);
        return new ResponseEntity(new CommonResponse(ex.getMessage(), ex.getMessage()), status);
    }

}
